"""
  (C) Copyright 2022-2024 Intel Corporation.

  SPDX-License-Identifier: BSD-2-Clause-Patent
"""
import os


class AvocadoException(Exception):
    """Exception for avocado utils methods."""


class AvocadoInfo():
    """Information about this version of avocado."""

    def __init__(self):
        """Initialize an AvocadoInfo object."""
        self.major = 0
        self.minor = 0

    def __str__(self):
        """Get the avocado version as a string.

        Returns:
            str: the avocado version
        """
        return f"Avocado {str(self.major)}.{str(self.minor)}"

    @staticmethod
    def set_config(overwrite=False):
        """Set up the avocado config files if they do not already exist.

        Should be called before get_setting() to ensure any files generated by this method are
        included.

        Args:
            overwrite (bool, optional): if true overwrite any existing avocado config files. If
                false do not modify any existing avocado config files. Defaults to False.

        Raises:
            AvocadoException: if there is an error writing an avocado config file
        """
        daos_base = os.getenv("DAOS_BASE", None)
        logs_dir = os.path.expanduser("~")
        if os.getenv("TEST_RPMS", "false").lower() == "true":
            logs_dir = os.path.join(os.sep, "var", "tmp", "ftest")
        elif daos_base:
            logs_dir = os.path.join(daos_base, "install", "lib", "daos", "TESTING", "ftest")

        job_results_dir = os.path.join(logs_dir, "avocado", "job-results")
        data_dir = os.path.join(logs_dir, "avocado", "data")
        config_dir = os.path.join(
            os.environ.get("VIRTUAL_ENV", os.path.expanduser("~")), ".config", "avocado")
        config_file = os.path.join(config_dir, "avocado.conf")
        sysinfo_dir = os.path.join(config_dir, "sysinfo")
        sysinfo_files_file = os.path.join(sysinfo_dir, "files")
        sysinfo_commands_file = os.path.join(sysinfo_dir, "commands")

        # Create the avocado configuration directories
        os.makedirs(config_dir, exist_ok=True)
        os.makedirs(sysinfo_dir, exist_ok=True)

        # Create the avocado config file. If one exists do not overwrite it.
        if not os.path.exists(config_file) or overwrite:
            # Give the avocado test tearDown method a minimum of 120 seconds to complete when the
            # test process has timed out.  The test harness will increment this timeout based upon
            # the number of pools created in the test to account for pool destroy command timeouts.
            config = [
                "[datadir.paths]\n",
                f"logs_dir = {job_results_dir}\n",
                f"data_dir = {data_dir}\n",
                "\n",
                "[job.output]\n",
                "loglevel = DEBUG\n",
                "\n",
                "[runner.timeout]\n",
                "after_interrupted = 120\n",
                "process_alive = 120\n",
                "process_died = 120\n",
                "\n",
                "[sysinfo.collectibles]\n",
                f"files = {sysinfo_files_file}\n",
                f"commands = {sysinfo_commands_file}\n",
            ]

            try:
                with open(config_file, "w", encoding="utf-8") as config_handle:
                    config_handle.writelines(config)
            except IOError as error:
                raise AvocadoException(
                    f"Error writing avocado config file {config_file}") from error

        # Create the avocado system info files file. If one exists do not overwrite it.
        if not os.path.exists(sysinfo_files_file) or overwrite:
            try:
                with open(sysinfo_files_file, "w", encoding="utf-8") as sysinfo_files_handle:
                    sysinfo_files_handle.write("/proc/mounts\n")
            except IOError as error:
                raise AvocadoException(
                    f"Error writing avocado config file {sysinfo_files_file}") from error

        # Create the avocado system info commands file. If one exists do not overwrite it.
        if not os.path.exists(sysinfo_commands_file) or overwrite:
            try:
                with open(sysinfo_commands_file, "w", encoding="utf-8") as sysinfo_commands_handle:
                    sysinfo_commands_handle.write("ps axf\n")
                    sysinfo_commands_handle.write("dmesg\n")
                    sysinfo_commands_handle.write("df -h\n")
            except IOError as error:
                raise AvocadoException(
                    f"Error writing avocado config file {sysinfo_commands_file}") from error

    def set_version(self):
        """Set the avocado major and minor versions"""
        # pylint: disable=import-outside-toplevel
        from avocado.core.version import MAJOR, MINOR
        self.major = int(MAJOR)
        self.minor = int(MINOR)

    @staticmethod
    def get_setting(section, key, default=None):
        """Get the value for the specified avocado setting.

        Args:
            section (str): avocado setting section name
            key (str): avocado setting key name
            default (object): default value to use if setting is undefined

        Returns:
            object: value for the avocado setting or None if not defined
        """
        # pylint: disable=import-outside-toplevel
        from avocado.core.settings import settings
        config = settings.as_dict()
        try:
            return config.get(".".join([section, key]))
        except KeyError:
            # Setting not found
            return default

    def get_logs_dir(self):
        """Get the avocado directory in which the test results are stored.

        Returns:
            str: the directory used by avocado to log test results
        """
        default_base_dir = os.path.join("~", "avocado", "job-results")
        return os.path.expanduser(
            self.get_setting("datadir.paths", "logs_dir", default_base_dir))

    def get_directory(self, directory):
        """Get the avocado test directory for the test.

        Args:
            directory (str): name of the sub directory to add to the logs directory

        Returns:
            str: the directory used by avocado to log test results
        """
        logs_dir = self.get_logs_dir()
        return os.path.join(logs_dir, directory)

    def get_list_command(self):
        """Get the avocado list command for this version of avocado.

        Returns:
            list: avocado list command parts
        """
        if self.major >= 83:
            return ["avocado", "list"]
        return ["avocado", "--paginator=off", "list"]

    def get_list_regex(self):
        """Get the regular expression used to get the test file from the avocado list command.

        Returns:
            str: regular expression to use to get the test file from the avocado list command output
        """
        if self.major >= 92:
            return r"avocado-instrumented\s+(.*):"
        return r"INSTRUMENTED\s+(.*):"

    def get_run_command(self, test, tag_filters, sparse, failfast):
        """Get the avocado run command for this version of avocado.

        Args:
            test (TestInfo): the test information
            tag_filters (list): optional '--filter-by-tags' arguments
            sparse (bool): whether or not to provide sparse output of the test execution
            failfast (bool): whether or not to fail fast

        Returns:
            list: avocado run command
        """
        command = ["avocado"]
        if not sparse:
            command.append("--show=test")
        command.append("run")
        command.append("--ignore-missing-references")
        if self.major >= 83:
            command.append("--disable-tap-job-result")
        else:
            command.extend(["--html-job-result", "on"])
            command.extend(["--tap-job-result", "off"])
        if tag_filters:
            command.extend(tag_filters)
        if failfast:
            command.append("--failfast")
        command.extend(["--mux-yaml", test.yaml_file])
        if test.extra_yaml:
            command.extend(test.extra_yaml)
        command.extend(["--", str(test)])
        return command
